/**
 * @version 2007-5-17
 * @author xalinx at gmail dot com
 */
package org.nanhill.commons.dbutil.builder;

import java.util.Iterator;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.nanhill.commons.dbutil.dialect.Dialect;
import org.nanhill.commons.dbutil.dialect.DialectFactory;

/**
 * @version 2007-5-17
 * @author xalinx at gmail dot com
 * 
 */
public class SqlBuilder {
	private NameMapper nameMapper;

	public void setNameMapper(NameMapper nameMapper) {
		this.nameMapper = nameMapper;
	}

	private Dialect dialect;

	public void setDialect(Dialect dialect) {
		this.dialect = dialect;
	}

	public void setDialectFactory(DialectFactory factory) {
		setDialect(factory.getDialect());
	}

	public <T> QueryWrap buildLimitSelect(BeanMonitor<T> beanMonitor, int start, int size) {
		QueryWrap wrap = buildSelect(beanMonitor);
		boolean offset = 0 == start ? false : true;
		wrap.setSql(dialect.getLimitSql(wrap.getSql(), offset));
		wrap.setValues(dialect.getLimitArguments(start, size, wrap.getValues()));
		return wrap;
	}

	public <T> QueryWrap buildSelect(BeanMonitor<T> monitor) {
		String tableName = getTableName(monitor);
		List<String> columns = monitor.getReadedPropertyNames();
		int columnNum = columns.size();
		StringBuilder builder = new StringBuilder(128);
		builder.append("select ");
		for (int i = 0; i < columnNum; i++) {
			builder.append(columns.get(i));
			if (i < columnNum - 1) {
				builder.append(",");
			}
		}
		builder.append(" from ");
		builder.append(tableName);

		buildWhereExpression(monitor, builder);

		Object[] values = monitor.getWritedPropertyValues().toArray(ArrayUtils.EMPTY_OBJECT_ARRAY);
		return new QueryWrap(builder.toString(), values);
	}

	public <T> QueryWrap buildDelete(BeanMonitor<T> monitor) {
		String tableName = getTableName(monitor);
		StringBuilder builder = new StringBuilder(128);
		builder.append("delete from ");
		builder.append(tableName);

		buildWhereExpression(monitor, builder);

		Object[] values = monitor.getWritedPropertyValues().toArray(ArrayUtils.EMPTY_OBJECT_ARRAY);
		return new QueryWrap(builder.toString(), values);
	}

	public <T> QueryWrap buildInsert(BeanMonitor<T> monitor) {
		String tableName = getTableName(monitor);
		List<String> columns = monitor.getWritedPropertyNames();
		int columnNum = columns.size();

		StringBuilder builder = new StringBuilder(128);
		builder.append("insert into ");
		builder.append(tableName);
		builder.append(" (");
		for (int i = 0; i < columnNum; i++) {
			builder.append(columns.get(i));
			if (i < columnNum - 1) {
				builder.append(",");
			}
		}
		builder.append(") values (");
		for (int i = 0; i < columnNum; i++) {
			builder.append("?");
			if (i < columnNum - 1) {
				builder.append(",");
			}
		}
		builder.append(")");

		Object[] values = monitor.getWritedPropertyValues().toArray(ArrayUtils.EMPTY_OBJECT_ARRAY);
		return new QueryWrap(builder.toString(), values);
	}

	public <E> QueryWrap buildCount(BeanMonitor<E> monitor) {
		String tableName = getTableName(monitor);
		StringBuilder builder = new StringBuilder(128);
		builder.append("select count(*) from ");
		builder.append(tableName);

		buildWhereExpression(monitor, builder);

		Object[] values = monitor.getWritedPropertyValues().toArray(ArrayUtils.EMPTY_OBJECT_ARRAY);
		return new QueryWrap(builder.toString(), values);
	}

	public <E> QueryWrap buildCount(Class<E> entity) {
		String tableName = getTableName(entity);
		return new QueryWrap("select count(*) from " + tableName, null);
	}

	/**
	 * 
	 * 
	 * @param monitor
	 * @param id
	 */
	public <E> QueryWrap buildUpdate(BeanMonitor<E> monitor) {
		List<String> columns = monitor.getWritedPropertyNames();
		String tableName = getTableName(monitor);
		int columnNum = monitor.getWhereOffset();

		StringBuilder builder = new StringBuilder(128);
		builder.append("update ");
		builder.append(tableName);
		builder.append(" set ");
		for (int i = 0; i < columnNum; i++) {
			builder.append(columns.get(i));
			builder.append("=?");
			if (i < columnNum - 1) {
				builder.append(",");
			}
		}

		buildWhereExpression(monitor, builder);

		Object[] values = monitor.getWritedPropertyValues().toArray(ArrayUtils.EMPTY_OBJECT_ARRAY);
		return new QueryWrap(builder.toString(), values);
	}

	private String getTableName(BeanMonitor<?> beanMonitor) {
		return nameMapper.getTableName(beanMonitor.getBean().getClass().getCanonicalName());
	}

	private String getTableName(Class<?> entity) {
		return nameMapper.getTableName(entity.getCanonicalName());
	}

	private <E> void buildWhereExpression(BeanMonitor<E> monitor, StringBuilder builder) {
		int whereOffset = monitor.getWhereOffset();
		List<String> columns = monitor.getWritedPropertyNames();
		// build where expression
		if (!CollectionUtils.isEmpty(columns) || whereOffset < columns.size()) {
			int columnNum = columns.size();
			builder.append(" where");

			Iterator<Condition> conditions = monitor.getConditions().iterator();
			for (int i = whereOffset; i < columnNum; i++) {
				String columnName = columns.get(i);
				Condition cond = null;
				while (true) {
					cond = conditions.next();
					if (!cond.isCompare()) {
						builder.append(" ");
						builder.append(cond.getExpression());
					} else {
						break;
					}
				}
				builder.append(" ");
				builder.append(columnName);
				builder.append(cond.getExpression());
				builder.append("?");
			}
		}
	}

}
